import * as THREE from 'https://unpkg.com/three@0.160.0/build/three.module.js';

// 基础场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0b1023);

const camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 100);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);

// 光照
const ambient = new THREE.AmbientLight(0xffffff, 0.9);
scene.add(ambient);
const dirLight = new THREE.DirectionalLight(0xffffff, 0.7);
dirLight.position.set(5, 8, 10);
scene.add(dirLight);

// 魔方参数
const UNIT = 1; // 单个小方块边长
const GAP = 0.04; // 间距

const cubeGroup = new THREE.Group();
scene.add(cubeGroup);

// 初始相机位置
camera.position.set(6.8, 5.2, 6.8);
camera.lookAt(0, 0, 0);

// 颜色（标准配色）：R/L/U/D/F/B
const COLORS = {
  R: 0xff3b30, // +X 红
  L: 0xff9f0a, // -X 橙
  U: 0xffffff, // +Y 白
  D: 0xffd60a, // -Y 黄
  F: 0x34c759, // +Z 绿
  B: 0x0a84ff, // -Z 蓝
  K: 0x0f172a, // 内部/未贴纸深色
};

// UI 引用
const $ = (sel) => document.querySelector(sel);
const btnScramble = $('#btn-scramble');
const btnReset = $('#btn-reset');
const btnUndo = $('#btn-undo');
const btnRedo = $('#btn-redo');
const statusEl = $('#status');
const speedInput = /** @type {HTMLInputElement} */ (document.getElementById('speed'));
const speedValEl = $('#speed-value');
const btnHelp = $('#btn-help');
const help = $('#help');
const btnCloseHelp = $('#btn-close-help');

let turnDurationMs = parseInt(speedInput.value, 10) || 280;
speedValEl.textContent = `${turnDurationMs}ms`;
speedInput.addEventListener('input', () => {
  turnDurationMs = parseInt(speedInput.value, 10);
  speedValEl.textContent = `${turnDurationMs}ms`;
});

btnHelp.addEventListener('click', () => help.classList.toggle('hidden'));
btnCloseHelp.addEventListener('click', () => help.classList.add('hidden'));

// 状态与交互
/** @type {THREE.Mesh[]} */
let cubies = [];
let isDragging = false; // 是否在拖拽（可能是整块或面层）
let isTurning = false; // 是否有动画转层进行
let dragStart = { x: 0, y: 0 };
let raycaster = new THREE.Raycaster();
let pointer = new THREE.Vector2();
let hoverInfo = { mesh: null, materialIndex: -1 };

let activeFaceInfo = null; // 面层拖拽起点信息
// { normal: THREE.Vector3, startPoint: THREE.Vector3, faceIndex: number, cubie: THREE.Mesh }

// 动作队列与撤销栈
const moveQueue = []; // 顺序执行的动作
const undoStack = [];
const redoStack = [];

// 工具函数
function nearlyEqual(a, b, eps = 1e-4) { return Math.abs(a - b) < eps; }
function sign(v) { return v >= 0 ? 1 : -1; }
function clamp(v, a, b) { return Math.min(Math.max(v, a), b); }
function roundToLayer(v) { // -1, 0, 1
  const r = Math.round(v);
  return clamp(r, -1, 1);
}

function createCubieMaterials(ix, iy, iz) {
  // BoxGeometry 材质顺序: [right +X, left -X, top +Y, bottom -Y, front +Z, back -Z]
  const mats = [];
  mats[0] = new THREE.MeshStandardMaterial({ color: ix === 1 ? COLORS.R : COLORS.K, metalness: 0.08, roughness: 0.6 });
  mats[1] = new THREE.MeshStandardMaterial({ color: ix === -1 ? COLORS.L : COLORS.K, metalness: 0.08, roughness: 0.6 });
  mats[2] = new THREE.MeshStandardMaterial({ color: iy === 1 ? COLORS.U : COLORS.K, metalness: 0.08, roughness: 0.5 });
  mats[3] = new THREE.MeshStandardMaterial({ color: iy === -1 ? COLORS.D : COLORS.K, metalness: 0.08, roughness: 0.5 });
  mats[4] = new THREE.MeshStandardMaterial({ color: iz === 1 ? COLORS.F : COLORS.K, metalness: 0.08, roughness: 0.6 });
  mats[5] = new THREE.MeshStandardMaterial({ color: iz === -1 ? COLORS.B : COLORS.K, metalness: 0.08, roughness: 0.6 });
  return mats;
}

function buildCube() {
  // 清空
  for (const c of cubies) cubeGroup.remove(c);
  cubies.length = 0;

  const geom = new THREE.BoxGeometry(UNIT, UNIT, UNIT);
  const step = UNIT + GAP;

  for (let ix = -1; ix <= 1; ix++) {
    for (let iy = -1; iy <= 1; iy++) {
      for (let iz = -1; iz <= 1; iz++) {
        const mats = createCubieMaterials(ix, iy, iz);
        const mesh = new THREE.Mesh(geom, mats);
        mesh.position.set(ix * step, iy * step, iz * step);
        mesh.castShadow = false; mesh.receiveShadow = false;
        mesh.userData.cubeIndex = { x: ix, y: iy, z: iz };
        cubies.push(mesh);
        cubeGroup.add(mesh);
      }
    }
  }
}

buildCube();

// 微弱地增强边缘效果（轮廓光）
const rimLight = new THREE.DirectionalLight(0x66ccff, 0.35);
rimLight.position.set(-6, -8, -10);
scene.add(rimLight);

// 交互：射线与悬浮高亮
function setPointerFromEvent(e) {
  const rect = renderer.domElement.getBoundingClientRect();
  const x = (('clientX' in e ? e.clientX : e.touches[0].clientX) - rect.left) / rect.width;
  const y = (('clientY' in e ? e.clientY : e.touches[0].clientY) - rect.top) / rect.height;
  pointer.set(x * 2 - 1, -(y * 2 - 1));
}

function updateHover() {
  if (isTurning || isDragging) return;
  raycaster.setFromCamera(pointer, camera);
  const intersects = raycaster.intersectObjects(cubies, false);

  // 清除旧 hover
  if (hoverInfo.mesh && hoverInfo.materialIndex >= 0) {
    const mats = hoverInfo.mesh.material;
    if (Array.isArray(mats) && mats[hoverInfo.materialIndex]) {
      mats[hoverInfo.materialIndex].emissive?.setHex(0x000000);
    }
    hoverInfo.mesh = null; hoverInfo.materialIndex = -1;
  }

  if (intersects.length) {
    const hit = intersects[0];
    const faceIndex = Math.floor(hit.faceIndex / 2); // BoxGeometry: 两个三角面合成一个方面
    const mats = hit.object.material;
    if (Array.isArray(mats) && mats[faceIndex]) {
      mats[faceIndex].emissive?.setHex(0x224466);
      hoverInfo.mesh = hit.object;
      hoverInfo.materialIndex = faceIndex;
    }
  }
}

// 面层拖拽辅助：基于初始面法线与相机方向，构建“右/上”切向基
function computeFaceAxes(normal, facePoint) {
  const viewDir = new THREE.Vector3().subVectors(camera.position, facePoint).normalize();
  // 右 = view x normal（观察该面时的屏幕向右）
  const right = new THREE.Vector3().crossVectors(viewDir, normal).normalize();
  // 上 = normal x right
  const up = new THREE.Vector3().crossVectors(normal, right).normalize();
  return { right, up };
}

// 从世界法线得到主轴名称
function axisNameFromNormal(n) {
  const ax = Math.abs(n.x), ay = Math.abs(n.y), az = Math.abs(n.z);
  if (ax > ay && ax > az) return 'x';
  if (ay > az) return 'y';
  return 'z';
}

// 将世界坐标转换为 cubeGroup 局部坐标
function worldToCubeLocal(pWorld) {
  const p = pWorld.clone();
  return cubeGroup.worldToLocal(p);
}

// 新增：将世界方向向量转换为 cubeGroup 局部方向
function worldDirToCubeLocal(dirWorld) {
  const q = cubeGroup.getWorldQuaternion(new THREE.Quaternion());
  const invQ = q.clone().invert();
  return dirWorld.clone().applyQuaternion(invQ).normalize();
}

// 选择一层：在主轴上与层值相近的 cubie
function pickLayer(axis, layerValue) {
  const eps = 0.5 * (UNIT + GAP);
  const axisIdx = axis;
  return cubies.filter(c => {
    const p = worldToCubeLocal(c.getWorldPosition(new THREE.Vector3()));
    if (axisIdx === 'x') return Math.abs(p.x - layerValue * (UNIT + GAP)) < eps;
    if (axisIdx === 'y') return Math.abs(p.y - layerValue * (UNIT + GAP)) < eps;
    return Math.abs(p.z - layerValue * (UNIT + GAP)) < eps;
  });
}

// 动画辅助
function animateRotation(target, axis, deltaAngle, duration) {
  return new Promise(resolve => {
    const start = performance.now();
    const from = target.rotation[axis];
    const to = from + deltaAngle;

    function easeInOutCubic(t) {
      return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
    }

    function step(now) {
      const t = clamp((now - start) / duration, 0, 1);
      const k = easeInOutCubic(t);
      target.rotation[axis] = from + (to - from) * k;
      if (t < 1) {
        requestAnimationFrame(step);
      } else {
        target.rotation[axis] = to;
        resolve();
      }
    }
    requestAnimationFrame(step);
  });
}

// 执行一步转层
async function performMove(axis, layerValue, quarterTurns) {
  if (isTurning) { // 入队
    moveQueue.push({ axis, layerValue, quarterTurns });
    return;
  }
  isTurning = true;

  const layerCubies = pickLayer(axis, layerValue);
  const pivot = new THREE.Group();
  cubeGroup.add(pivot);
  pivot.updateMatrixWorld();

  // 将该层的方块 attach 到 pivot，以保持世界变换
  for (const c of layerCubies) pivot.attach(c);

  const angle = (Math.PI / 2) * quarterTurns; // 可为 -1/1 或 -2
  await animateRotation(pivot, axis, angle, turnDurationMs);

  // 收尾：将 cubie 从 pivot 还原回 cubeGroup；位置对齐到网格
  for (const c of layerCubies) {
    cubeGroup.attach(c);
    // 对齐位置到 -1/0/1 层中心
    const p = worldToCubeLocal(c.getWorldPosition(new THREE.Vector3()));
    const ix = roundToLayer(Math.round(p.x / (UNIT + GAP)));
    const iy = roundToLayer(Math.round(p.y / (UNIT + GAP)));
    const iz = roundToLayer(Math.round(p.z / (UNIT + GAP)));
    c.position.copy(new THREE.Vector3(ix, iy, iz).multiplyScalar(UNIT + GAP));
    // 注意：不重置旋转，贴纸朝向应保留
    c.userData.cubeIndex = { x: ix, y: iy, z: iz };
  }

  cubeGroup.remove(pivot);
  isTurning = false;

  // 继续执行队列
  if (moveQueue.length) {
    const next = moveQueue.shift();
    await performMove(next.axis, next.layerValue, next.quarterTurns);
  }
}

// 对外调用（带撤销/重做记录）
async function doMove(axis, layerValue, quarterTurns, record = true) {
  await performMove(axis, layerValue, quarterTurns);
  if (record) {
    undoStack.push({ axis, layerValue, quarterTurns });
    redoStack.length = 0;
  }
}

function inverseMove(m) { return { axis: m.axis, layerValue: m.layerValue, quarterTurns: -m.quarterTurns }; }

btnUndo.addEventListener('click', async () => {
  if (isTurning || !undoStack.length) return;
  const m = undoStack.pop();
  const inv = inverseMove(m);
  await doMove(inv.axis, inv.layerValue, inv.quarterTurns, false);
  redoStack.push(m);
});

btnRedo.addEventListener('click', async () => {
  if (isTurning || !redoStack.length) return;
  const m = redoStack.pop();
  await doMove(m.axis, m.layerValue, m.quarterTurns, true);
});

btnReset.addEventListener('click', async () => {
  if (isTurning) return;
  statusEl.textContent = '复原中…';
  // 直接重建初始状态
  buildCube();
  undoStack.length = 0; redoStack.length = 0; moveQueue.length = 0;
  statusEl.textContent = '已复原';
});

btnScramble.addEventListener('click', async () => {
  if (isTurning) return;
  const moves = generateScramble(25);
  statusEl.textContent = `打乱中 0/${moves.length}`;
  for (let i = 0; i < moves.length; i++) {
    const m = moves[i];
    await doMove(m.axis, m.layerValue, m.quarterTurns, true);
    statusEl.textContent = `打乱中 ${i + 1}/${moves.length}`;
  }
  statusEl.textContent = '打乱完成';
});

// 生成打乱序列（避免连续同轴同层重复）
function generateScramble(n = 25) {
  const axes = ['x', 'y', 'z'];
  const layers = [-1, 0, 1];
  const turns = [-2, -1, 1, 2];
  const seq = [];
  for (let i = 0; i < n; i++) {
    let candidate;
    do {
      candidate = {
        axis: axes[Math.floor(Math.random() * axes.length)],
        layerValue: layers[Math.floor(Math.random() * layers.length)],
        quarterTurns: turns[Math.floor(Math.random() * turns.length)],
      };
    } while (seq.length && seq[seq.length - 1].axis === candidate.axis && seq[seq.length - 1].layerValue === candidate.layerValue);
    seq.push(candidate);
  }
  return seq;
}

// 指针事件：区分在魔方面上拖拽（转层）与空白拖拽（整块旋转）
let rotatingWholeCube = false;
let lastPointer = { x: 0, y: 0 };

function onPointerDown(e) {
  if (isTurning) return;
  isDragging = true;
  setPointerFromEvent(e);
  dragStart.x = pointer.x; dragStart.y = pointer.y;
  lastPointer.x = ('clientX' in e ? e.clientX : e.touches[0].clientX);
  lastPointer.y = ('clientY' in e ? e.clientY : e.touches[0].clientY);

  raycaster.setFromCamera(pointer, camera);
  const intersects = raycaster.intersectObjects(cubies, false);
  if (intersects.length) {
    const hit = intersects[0];
    // 计算世界法线
    const worldNormal = hit.face.normal.clone().transformDirection(hit.object.matrixWorld).normalize();
    const startPoint = hit.point.clone();
    activeFaceInfo = { normal: worldNormal, startPoint, faceIndex: Math.floor(hit.faceIndex / 2), cubie: hit.object };
    rotatingWholeCube = false;
  } else {
    activeFaceInfo = null;
    rotatingWholeCube = true; // 在空白处拖，旋转整块
  }
}

function onPointerMove(e) {
  setPointerFromEvent(e);
  updateHover();

  if (!isDragging || isTurning) return;

  const cx = ('clientX' in e ? e.clientX : e.touches[0].clientX);
  const cy = ('clientY' in e ? e.clientY : e.touches[0].clientY);
  const dx = cx - lastPointer.x;
  const dy = cy - lastPointer.y;
  lastPointer.x = cx; lastPointer.y = cy;

  if (rotatingWholeCube) {
    // 旋转整块（基于屏幕拖拽）
    const rotSpeed = 0.0055;
    cubeGroup.rotation.y += dx * rotSpeed;
    cubeGroup.rotation.x += dy * rotSpeed;
    return;
  }

  // 面上拖拽：当拖拽位移达到阈值时，判定一次 90° 转动
  if (activeFaceInfo) {
    const { normal, startPoint, cubie } = activeFaceInfo;
    // 用通过 startPoint、法线的平面，求当前指针与该平面的交点
    const plane = new THREE.Plane().setFromNormalAndCoplanarPoint(normal, startPoint);
    raycaster.setFromCamera(pointer, camera);
    const intersectPoint = new THREE.Vector3();
    raycaster.ray.intersectPlane(plane, intersectPoint);
    if (!intersectPoint) return;
    const delta = new THREE.Vector3().subVectors(intersectPoint, startPoint);

    const threshold = 0.18; // 触发阈值
    if (delta.length() < threshold) return;

    // 计算面内“右/上”方向
    const { right, up } = computeFaceAxes(normal, startPoint);
    const dRight = delta.dot(right);
    const dUp = delta.dot(up);

    // 角度方向规则：
    // - 水平为主：向右拖 => 绕法线负向（屏幕视角 CW）；向左反之
    // - 垂直为主：向上拖 => 绕法线正向；向下反之
    const angleSign = (Math.abs(dRight) > Math.abs(dUp)) ? -sign(dRight) : sign(dUp);

    // 关键：把世界法线转到 cubeGroup 局部来决定主轴
    const localNormal = worldDirToCubeLocal(normal);
    const axis = axisNameFromNormal(localNormal);
    // 取层值：命中 cubie 在该轴上的层（-1/0/1）
    const localP = worldToCubeLocal(cubie.getWorldPosition(new THREE.Vector3()));
    const layerValue = axis === 'x' ? roundToLayer(Math.round(localP.x / (UNIT + GAP)))
                      : axis === 'y' ? roundToLayer(Math.round(localP.y / (UNIT + GAP)))
                      : roundToLayer(Math.round(localP.z / (UNIT + GAP)));

    // 执行一步
    doMove(axis, layerValue, angleSign);

    // 结束此次面拖拽，避免持续触发
    activeFaceInfo = null;
    isDragging = false;
  }
}

function onPointerUp() {
  isDragging = false;
  activeFaceInfo = null;
}

renderer.domElement.addEventListener('mousedown', onPointerDown);
window.addEventListener('mousemove', onPointerMove);
window.addEventListener('mouseup', onPointerUp);
renderer.domElement.addEventListener('touchstart', onPointerDown, { passive: true });
window.addEventListener('touchmove', onPointerMove, { passive: true });
window.addEventListener('touchend', onPointerUp, { passive: true });

// 自适应
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});

// 动画循环
function tick() {
  renderer.render(scene, camera);
  requestAnimationFrame(tick);
}
requestAnimationFrame(tick);

// 初始状态提示
statusEl.textContent = '准备就绪 - 在面上拖拽可转层，在空白处拖拽可旋转整块'; 